package com.up.mongo.core;

import com.mongodb.BasicDBObject;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Title:
 * @Author: fxp
 * @Date: 2022/11/15 14:56
 * @Desc:
 */
public class AssociationQueryBuilder {
    /**
     * 本地集合过了条件 传字段和值 精确匹配
     */
    private final List<MongoField> localMatchFields = new ArrayList<>();
    /**
     * 本表排序字段
     */
    private final List<MongoSortField> localSorts = new ArrayList<>();
    /**
     * 本地集合字段与关联集合字段映射 精确匹配
     */
    private final List<MongoField> conditionFields = new ArrayList<>();

    /**
     * 关联表排序字段
     */
    private final List<MongoSortField> fromSorts = new ArrayList<>();
    /**
     * 关联表
     */
    private final String fromCol;
    /**
     * 关联表查询出来的值存放的字段名
     */
    private final String asField;

    public AssociationQueryBuilder(String fromCol,String asField){
        this.fromCol = fromCol;
        this.asField = asField;
    }


    /**
     * 创建关联查询构建器对象
     * @param fromCol
     * @param asField
     * @return
     */
    public static AssociationQueryBuilder create(String fromCol,String asField){
        return new AssociationQueryBuilder(fromCol,asField);
    }

    /**
     * 添加本地集合的查询条件
     * @param localField
     * @param filedValue
     * @return
     */
    public AssociationQueryBuilder appendLocalMatch(String localField, Object filedValue){
        localMatchFields.add(new MongoField(localField,filedValue));
        return this;
    }
    /**
     * 添加本地排序字段
     * @param localField
     * @param isAsc
     * @return
     */
    public AssociationQueryBuilder appendLocalFieldSort(String localField, Boolean isAsc){
        localSorts.add(new MongoSortField(localField,isAsc));
        return this;
    }
    /**
     * 添加关联表查询结果排序字段
     * @param localField
     * @param isAsc
     * @return
     */
    public AssociationQueryBuilder appendFromFieldSort(String localField, Boolean isAsc){
        fromSorts.add(new MongoSortField(localField,isAsc));
        return this;
    }

    /**
     * 添加关联字段
     * @param localField
     * @param foreignField
     * @return
     */
    public AssociationQueryBuilder appendRelationMatch(String localField, String foreignField){
        conditionFields.add(new MongoField(localField,foreignField));
        return this;
    }

    /**
     * 构建本地集合查询条件对象
     * @return
     */
    private BasicDBObject buildLocalMatch(){
        BasicDBObject match = new BasicDBObject();
        BasicDBObject fieldsMatch=new BasicDBObject();
        localMatchFields.forEach(field -> {
            fieldsMatch.append(field.getLocalField(), field.getFiledValue());

        });
        match.put("$match",fieldsMatch);
        return match;
    }

    /**
     * 构建本地排序字段
     * @return
     */
    private BasicDBObject buildLocalSorts(){
        return this.buildSortField(this.localSorts);
    }

    /**
     * 构建中间查询变量对象
     * @return
     */
    private BasicDBObject buildLet(){
        BasicDBObject letFiled=new BasicDBObject();
        conditionFields.forEach(field -> {
            letFiled.append(field.getLocalField(), "$"+field.getLocalField());

        });
        return letFiled;
    }

    /**
     * 构建关联查询条件对象
     * @return
     */
    private List<BasicDBObject> buildRelationMatch(){
        List<BasicDBObject> conditions =  new ArrayList<>();//条件
        conditionFields.forEach(field -> {
            conditions.add(new BasicDBObject("$eq", Arrays.asList("$"+field.getForeignField(),"$$"+field.getLocalField())));
        });
        return conditions;
    }

    /**
     * 构建关联表排序字段
     * @return
     */
    private BasicDBObject buildFromSorts(){
        return this.buildSortField(this.fromSorts);
    }

    /**
     * 构建排序字段
     * @param sortFields
     * @return
     */
    private BasicDBObject buildSortField(List<MongoSortField> sortFields){
        BasicDBObject sorts = new BasicDBObject();
        BasicDBObject fieldsMatch=new BasicDBObject();
        sortFields.forEach(field -> {
            fieldsMatch.append(field.getField(), field.getAsc()?1:-1);

        });
        sorts.put("$sort",fieldsMatch);
        return sorts;
    }

    /**
     * 构建聚合查询
     * @return
     */
    public List<BasicDBObject> build(){
        /**
         * 构建本表查询条件
         */
        List<BasicDBObject> aggregateVar = new ArrayList<BasicDBObject>();
        aggregateVar.add(buildLocalMatch());

        /**
         * 构建关联
         */
        BasicDBObject lookup = new BasicDBObject();//聚合
        lookup.append("from", this.fromCol)//设置关联表
                .append("let", buildLet());//设置中间变脸

        List<BasicDBObject> pipelines = new ArrayList<>();//管道聚合
        pipelines.add(new BasicDBObject("$match", new BasicDBObject("$expr",new BasicDBObject("$and",buildRelationMatch()))));
        if(!CollectionUtils.isEmpty(this.fromSorts)){
            pipelines.add(this.buildFromSorts());
        }
        lookup.append("pipeline", pipelines);
        lookup.append("as",this.asField);

        BasicDBObject lookupWapper = new BasicDBObject("$lookup",lookup);//聚合

        aggregateVar.add(lookupWapper);

        if(!CollectionUtils.isEmpty(this.localSorts)){
            pipelines.add(this.buildLocalSorts());
        }

        return aggregateVar;
    }

}
